#include "glshaderobject.h"
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include "glutils.h"

using namespace std;

glShaderObject::glShaderObject()
{
    compiler_log = 0;
    is_compiled = false;
    program_type = 0;
    ShaderObject = 0;
    ShaderSource = 0;
    _memalloc = false;
}

glShaderObject::~glShaderObject()
{
    if (compiler_log != 0) free(compiler_log);
    if (ShaderSource != 0)
    {
        if (_memalloc)
            delete[] ShaderSource;
    }

    if (is_compiled)
    {
        glDeleteShader(ShaderObject);
        CHECK_GL_ERROR();
    }
}

unsigned long getFileLength(ifstream& file)
{
    if (!file.good()) return 0;
    unsigned long pos=file.tellg();
    file.seekg(0,ios::end);
    unsigned long len = file.tellg();
    file.seekg(ios::beg);
    return len;
}

int glShaderObject::load(char* filename)
{
    ifstream file;
    file.open(filename, ios::in);
    if(!file) return -1;

    unsigned long len = getFileLength(file);

    if (len==0) return -2;   // "Empty File"

    if (ShaderSource!=0)    // there is already a source loaded, free it!
    {
        if (_memalloc)
            delete[] ShaderSource;
    }

    ShaderSource = (GLubyte*) new char[len+1];
    if (ShaderSource == 0) return -3;   // can't reserve memory
    _memalloc = true;

    ShaderSource[len] = 0;  // len isn't always strlen cause some characters are stripped in ascii read...
    // it is important to 0-terminate the real length later, len is just max possible value...
    unsigned int i=0;
    while (file.good())
    {
        ShaderSource[i] = file.get();       // get character from file.
        if (!file.eof())
            i++;
    }

    ShaderSource[i] = 0;  // 0 terminate it.

    file.close();

    return 0;
}

void glShaderObject::loadFromMemory(const char* program)
{
    if (ShaderSource!=0)    // there is already a source loaded, free it!
    {
        if (_memalloc)
            delete[] ShaderSource;
    }
    _memalloc = false;
    ShaderSource = (GLubyte*) program;

}

// Compiler Log: Ausgabe der Compiler Meldungen in String
char* glShaderObject::getCompilerLog(void)
{
    if (!useGLSL) return aGLSLStrings[0];

    GLint blen = 0;
    GLsizei slen = 0;


    if (ShaderObject==0) return aGLSLStrings[1]; // not a valid program object

    glGetShaderiv(ShaderObject, GL_INFO_LOG_LENGTH , &blen);
    CHECK_GL_ERROR();

    if (blen > 1)
    {
        if (compiler_log!=0)
        {
            free(compiler_log);
            compiler_log =0;
        }
        if ((compiler_log = (GLcharARB*)malloc(blen)) == NULL)
        {
            printf("ERROR: Could not allocate compiler_log buffer\n");
            return aGLSLStrings[3];
        }

        glGetInfoLogARB(ShaderObject, blen, &slen, compiler_log);
        CHECK_GL_ERROR();
        //cout << "compiler_log: \n", compiler_log);
    }
    if (compiler_log!=0)
        return (char*) compiler_log;
    else
        return aGLSLStrings[6];

    return aGLSLStrings[4];
}

bool glShaderObject::compile(void)
{
    if (!useGLSL) return false;

    is_compiled = false;

    GLint compiled = 0;

    if (ShaderSource==0) return false;

    GLint	length = (GLint) strlen((const char*)ShaderSource);
    glShaderSourceARB(ShaderObject, 1, (const GLcharARB **)&ShaderSource, &length);
    CHECK_GL_ERROR();

    glCompileShaderARB(ShaderObject);
    CHECK_GL_ERROR();
    glGetObjectParameterivARB(ShaderObject, GL_COMPILE_STATUS, &compiled);
    CHECK_GL_ERROR();

    if (compiled) is_compiled=true;

    return is_compiled;
}

GLint glShaderObject::getAttribLocation(char* attribName)
{
   return glGetAttribLocationARB(ShaderObject, attribName);
}

// ----------------------------------------------------------------------------
aVertexShader::aVertexShader()
{
    program_type = 1;
    if (useGLSL)
    {
        ShaderObject = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
        CHECK_GL_ERROR();
    }
}

aVertexShader::~aVertexShader()
{
}

// ----------------------------------------------------
aFragmentShader::aFragmentShader()
{
    program_type = 2;
    if (useGLSL)
    {
        ShaderObject = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
        CHECK_GL_ERROR();
    }
}

aFragmentShader::~aFragmentShader()
{
}

// ----------------------------------------------------
aGeometryShader::aGeometryShader()
{
    program_type = 3;
    if (useGLSL && bGeometryShader)
    {
        ShaderObject = glCreateShaderObjectARB(GL_GEOMETRY_SHADER_EXT);
        CHECK_GL_ERROR();
    }
}

aGeometryShader::~aGeometryShader()
{
}



